#include "Layer.h"

#include "../SGD Wrappers/CSGD_TextureManager.h"
#include "../tinyxml/tinyxml.h"

#include "Game.h"
#include "Camera.h"

CLayer::CLayer(void)
{
	this->m_nTopLeft_Index = 0;
	this->SetType(OBJ_LAYER);

	
	////////////Making a 25x20 grid of 64x64 tiles/////////////////////////
	///////////TESTING PURPOSES ONLY!//////////////////////////////////////
	this->SetWidth(25);
	this->SetHeight(20);
	this->m_nTileImgID = CSGD_TextureManager::GetInstance()->LoadTexture(_T("resource/Images/TileTestSet.bmp"));

	this->m_pTiles = new CTile[500];
	for(int x=0; x<500; ++x)
	{
		this->m_pTiles[x].SetCollidable((x<50 || x>275) ? true : false);

		if(25<=x && x<50)
		{
			this->m_pTiles[x].SetTrigger("Go Red");
			this->m_pTiles[x].SetHasEvent(true);
		}

		this->m_pTiles[x].SetHeight(64);
		this->m_pTiles[x].SetWidth(64);
		this->m_pTiles[x].SetImageID(this->m_nTileImgID);
		
		RECT testSourceRect = 
		{
			(x%8)%4*this->m_pTiles[x].GetWidth(),
			(x%8)/4*this->m_pTiles[x].GetHeight(),
			(x%8)%4*this->m_pTiles[x].GetWidth()+this->m_pTiles[x].GetWidth(),
			(x%8)/4*this->m_pTiles[x].GetHeight()+this->m_pTiles[x].GetHeight()
		};
		this->m_pTiles[x].SetSourceRect(testSourceRect);
		this->m_pTiles[x].SetX(x%this->GetWidth()*this->m_pTiles[x].GetWidth()+float(this->m_pTiles[x].GetWidth())/2);
		this->m_pTiles[x].SetY(x/this->GetWidth()*this->m_pTiles[x].GetHeight()+float(this->m_pTiles[x].GetHeight())/2);
	}

	//Saving and loading the current setting for these tiles
	//this->SaveTemp("Resource/Files/TestTileMap.xml");
	this->LoadTiles("Resource/Files/EditorTest.xml");
}

CLayer::~CLayer(void)
{
	if(this->m_nTileImgID != -1)
	{
		CSGD_TextureManager::GetInstance()->UnloadTexture(this->m_nTileImgID);
		this->m_nTileImgID = -1;
	}
}

void CLayer::Render()
{

	CGame* pGame = CGame::GetInstance();
	
	//tiles that are not currently on screen are culled from the rendering process
	for(int x=this->m_nTopLeft_Index, rows_skip = 0; this->m_pTiles[x].GetY()<pGame->GetScreenHeight()+(this->m_pTiles[x].GetHeight()*3/2);)
	{
		this->m_pTiles[x].Render();
	
		if(this->m_pTiles[x].GetX()<pGame->GetScreenWidth()-this->m_pTiles[x].GetWidth()/2	)
			++x;
		else
		{
			++rows_skip;
			x = this->m_nTopLeft_Index+this->GetWidth()*rows_skip;
		}
	
		//error check for out-of-bounds
		if(x>=this->GetHeight()*this->GetWidth())
			break;
	}
}

void CLayer::Update(float fElapsedTime)
{
	//I'll need this for culling
	CGame* pGame = CGame::GetInstance();

	//Update the tiles
	for(int x=0; x<this->GetWidth()*this->GetHeight(); ++x)
	{
		this->m_pTiles[x].SetVelX(CCamera::GetInstance()->GetSpeedModifier());
		this->m_pTiles[x].Update(fElapsedTime);
	}

	//Check if a different tile is being rendered at the upper left
	if(this->m_pTiles[this->m_nTopLeft_Index].GetX() < -this->m_pTiles[this->m_nTopLeft_Index].GetWidth()/2	)
	{
		//error check
		if(this->m_nTopLeft_Index < this->GetHeight()*this->GetWidth()-1)
			++this->m_nTopLeft_Index;
	}
	else if(this->m_pTiles[this->m_nTopLeft_Index].GetX() > this->m_pTiles[this->m_nTopLeft_Index].GetWidth()/2)
	{
		if(this->m_nTopLeft_Index > 0)
			--this->m_nTopLeft_Index;
	}

	if(this->m_pTiles[this->m_nTopLeft_Index].GetY() < -this->m_pTiles[this->m_nTopLeft_Index].GetHeight()	)
	{
		if(this->m_nTopLeft_Index < this->GetHeight()*this->GetWidth() - this->GetWidth())
			this->m_nTopLeft_Index += this->GetWidth();
	}
	else if(this->m_pTiles[this->m_nTopLeft_Index].GetY() > 0	)
	{
		if(this->m_nTopLeft_Index > this->GetWidth())
			this->m_nTopLeft_Index -= this->GetWidth();
	}
}

bool CLayer::CheckCollision(IEntity* pOther)
{
	CGame* pGame = CGame::GetInstance();

	//tiles that are not currently on screen are culled from the collision process
	for(int x=this->m_nTopLeft_Index, rows_skip = 0; this->m_pTiles[x].GetY()<pGame->GetScreenHeight()+this->m_pTiles[x].GetHeight(); )
	{
		if(this->m_pTiles[x].GetCollidable())
			if(this->m_pTiles[x].CheckCollision(pOther))
				return true;

		if(this->m_pTiles[x].GetX()<pGame->GetScreenWidth()-this->m_pTiles[x].GetWidth())
			++x;
		else
		{
			++rows_skip;
			x = this->m_nTopLeft_Index+this->GetWidth()*rows_skip;
		}

		//error check for out-of-bounds
		if(x>=this->GetHeight()*this->GetWidth())
			break;
	}

	return false;
}

//////////////////////////////////////////////////////
////Shouldn't ever need again, but here Justin Case///
//////////////////////////////////////////////////////
/*void CLayer::SaveTemp(const char* szFileName)
{
	//Make a new file with defualt names and scores
	
	// Create the TinyXML Document
	TiXmlDocument doc;

	// Create the XML Version (declaration)
	TiXmlDeclaration* pDecl = new TiXmlDeclaration("1.0", "utf-8", "");

	// Append the declaration to the document
	doc.LinkEndChild(pDecl);

	// Create a root node for the "layer"
	TiXmlElement* pLayerRoot = new TiXmlElement("layer");

	// Append the root to the document
	doc.LinkEndChild(pLayerRoot);

	//Create a node for the layer info
	TiXmlElement* pLayer = new TiXmlElement("layer_info");

	//Fill in the node with layer info
	pLayer->SetAttribute("Height", this->GetHeight());
	pLayer->SetAttribute("Width", this->GetWidth());

	//Create a Text node for the image name
	TiXmlText* pText = new TiXmlText("TileTestSet.bmp");
	pLayer->LinkEndChild(pText);

	//Append the layer node to the root
	pLayerRoot->LinkEndChild(pLayer);

	//Store all 500 tiles and their data
	for(int x = 0; x<this->GetHeight()*this->GetWidth(); ++x)
	{
		// Create a node for the tile
		TiXmlElement* pTile = new TiXmlElement("tile_info");

		// Fill in the node with tile info
		pTile->SetAttribute("Collidable", this->m_pTiles[x].GetCollidable());
		pTile->SetAttribute("HasEvent", this->m_pTiles[x].GetHasEvent());
		pTile->SetAttribute("Height", this->m_pTiles[x].GetHeight());
		pTile->SetAttribute("SourceRect_Left", this->m_pTiles[x].GetSourceRect().left);
		pTile->SetAttribute("SourceRect_Top", this->m_pTiles[x].GetSourceRect().top);
		pTile->SetAttribute("SourceRect_Right", this->m_pTiles[x].GetSourceRect().right);
		pTile->SetAttribute("SourceRect_Bottom", this->m_pTiles[x].GetSourceRect().bottom);
		pTile->SetAttribute("Trigger", this->m_pTiles[x].GetTrigger());
		pTile->SetAttribute("Width", this->m_pTiles[x].GetWidth());
		pTile->SetAttribute("X", int(this->m_pTiles[x].GetX()));
		pTile->SetAttribute("Y", int(this->m_pTiles[x].GetY()));

		// Create a Text node for the tile's event
		TiXmlText* pText = new TiXmlText(this->m_pTiles[x].GetTrigger());
		pTile->LinkEndChild( pText );

		// Append the layer node to the root
		pLayerRoot->LinkEndChild(pTile);
	}

	// Save the XML document to the file
	doc.SaveFile(szFileName);
}*/
bool CLayer::LoadTiles(const char* szFileName)
{
	int Int;
	const char* Char;
	double Double;
	RECT Rect;


	// Create the TinyXML Document
	TiXmlDocument doc;

	// Attempt to load from the file
	if(!doc.LoadFile(szFileName))
		return false;


	// Access the root element
	TiXmlElement* pRoot = doc.RootElement();

	if(pRoot == nullptr)
		return false;

	// load layer data
	TiXmlElement* pLayer = pRoot->FirstChildElement("layer_info");
	///////////////////////////
	TiXmlElement* pImageName = pLayer->FirstChildElement("image_name");
	string imagePath = pImageName->GetText();
	imagePath = "Resource/Images/" + imagePath;
	TCHAR* path = new TCHAR[imagePath.size()+1];
	CSTR_TO_TSTR(path, 2*(imagePath.size()+1), imagePath.c_str());
	this->m_nTileImgID = CSGD_TextureManager::GetInstance()->LoadTexture(path);
	///////////////////////////
	pLayer->Attribute("Height", &Int);
	this->SetHeight(Int);
	///////////////////////////
	pLayer->Attribute("Width", &Int);
	this->SetWidth(Int);
	///////////////////////////

	this->m_pTiles = new CTile[this->GetHeight()*this->GetWidth()];

	// load tile data
	TiXmlElement* pTile = pRoot->FirstChildElement("tile_info");
	for(int x=0; x<this->GetHeight()*this->GetWidth(); ++x)
	{
		///////////////////////////
		this->m_pTiles[x].SetImageID(this->m_nTileImgID);
		///////////////////////////
		this->m_pTiles[x].SetTrigger(pTile->Attribute("Trigger"));
		///////////////////////////
		pTile->Attribute("Collidable", &Int);
		this->m_pTiles[x].SetCollidable(Int==0 ? false: true);
		///////////////////////////
		pTile->Attribute("HasEvent", &Int);
		this->m_pTiles[x].SetHasEvent(Int==0 ? false: true);
		///////////////////////////
		pTile->Attribute("Height", &Int);
		this->m_pTiles[x].SetHeight(Int);
		///////////////////////////
		pTile->Attribute("SourceRect_Left", &Int);
		Rect.left = LONG(Int);
		///////////////////////////
		pTile->Attribute("SourceRect_Top", &Int);
		Rect.top = LONG(Int);
		///////////////////////////
		pTile->Attribute("SourceRect_Right", &Int);
		Rect.right = LONG(Int);
		///////////////////////////
		pTile->Attribute("SourceRect_Bottom", &Int);
		Rect.bottom = LONG(Int);
		this->m_pTiles[x].SetSourceRect(Rect);
		///////////////////////////
		pTile->Attribute("Width", &Int);
		this->m_pTiles[x].SetWidth(Int);
		///////////////////////////
		pTile->Attribute("X", &Double);
		this->m_pTiles[x].SetX(float(Double));
		///////////////////////////
		pTile->Attribute("Y", &Double);
		this->m_pTiles[x].SetY(float(Double));


		// Move to the next "tile_info" node
		pTile = pTile->NextSiblingElement("tile_info");
	}
	return true;
}